package cn.itcast.wanxinp2p.account.service.impl;


import cn.itcast.wanxinp2p.account.common.AccountErrorCode;
import cn.itcast.wanxinp2p.account.entity.pojo.Account;
import cn.itcast.wanxinp2p.account.mapper.AccountTccMapper;
import cn.itcast.wanxinp2p.account.service.AccountService;
import cn.itcast.wanxinp2p.account.mapper.AccountMapper;
import cn.itcast.wanxinp2p.common.domain.RestResponse;
import cn.itcast.wanxinp2p.common.domain.StatusCode;
import cn.itcast.wanxinp2p.common.exception.BusinessException;
import cn.itcast.wanxinp2p.common.util.PasswordUtil;
import cn.itcast.wanxinp2p.dto.account.AccountDTO;
import cn.itcast.wanxinp2p.dto.account.AccountLoginDTO;
import cn.itcast.wanxinp2p.dto.account.AccountRegisterDTO;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hmily.annotation.Hmily;
import org.dromara.hmily.core.concurrent.threadlocal.HmilyTransactionContextLocal;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 六一居士
 */
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {


    /**
     *  注入restTemplate
     */
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 注入Mapper
     */
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountTccMapper accountTccMapper;

    @Value("${sms.enable}")
    private boolean smsEnable;
    @Value("${sms.url}")
    private String smsUrl;

    /**
     * 发起远程请求调用 短信微服务 发送短信
     * @param mobile 手机号
     * @return 信息响应类
     */
    @Override
    public RestResponse getSMSCode(String mobile) {
        //TODO
        // 发起远程请求调用 短信微服务 发送短信   RestTemplate
        // http://localhost:56085/sailing/generate?effectiveTime=300&name=sms
        // {"mobile":"13566668888"}
        Map<String,String> map = new HashMap<>();
        map.put("mobile",mobile);
        String url = "http://localhost:56085/sailing/generate?effectiveTime=300&name=sms";
        RestResponse restResponse = restTemplate.postForObject(url, map, RestResponse.class);
        return restResponse;
    }


    /**
     * 校验手机号和验证码
     * @param mobile 手机号
     * @param key 校验标识
     * @param code 验证码
     * @return 1(成功) / 0(失败)
     */
    @Override
    public Integer checkMobile(String mobile, String key, String code) {

        //1、定义校验路径
        String url = "http://localhost:56085/sailing/verify?name=sms&verificationKey="+key+"&verificationCode="+code;
        //2、发起resttemplate远程请求，获取响应结果
//        ResponseEntity<RestResponse> response = restTemplate.postForEntity(url, null, RestResponse.class);
//        //3、获取响应内容
//        RestResponse body = response.getBody();
//        //4、获取校验结果
//        Boolean result = (Boolean) body.getResult();
//        if (!result){
//            throw new BusinessException(AccountErrorCode.E_140152);
//        }
        RestResponse restResponse = restTemplate.postForObject(url, null, RestResponse.class);
        if (!restResponse.isSuccessful()){
            throw new BusinessException(AccountErrorCode.E_140152);
        }
        //查询有没有该手机号
        Account account = accountMapper.getMobileOne(mobile);
        return account!=null?1:0;

        /* 1、远程调用 短信微服务 校验验证码是否正确
         Post请求：
         http://localhost:56085/sailing/verify?name=sms&verificationKey=sms:70ba933b095b4b5c90ee314045fd9003&verificationCode=707339
         根据结果判断验证码校验是否成功，如果失败，则抛出 AccountErrorCode.E_140152 异常

          2、如果验证码正确，还需要根据手机号 去 account 表中查询手机号是否存在
            	如果存在返回 1，否则返回 0
        */

       /* 返回结果：
        {
            "code": 0,
            "msg": "",
            "result": 1(成功) / 0(失败)
        }
        */
    }


    /**
     * 统一账号服务实现保存功能
     * @param accountRegisterDTO 客户端用户传输的基础数据
     * @return 返回用户数据
     */
    @Override
    //只要标记@Hmily就是try方法，在注解中指定confirm、cancel两个方法的名字
    @Hmily(confirmMethod = "confirmMethod",cancelMethod = "cancelMethod")
    @Transactional
    public AccountDTO register(AccountRegisterDTO accountRegisterDTO) {

        String transId = HmilyTransactionContextLocal.getInstance().get().getTransId();
        log.info("账号服务 try begin 开始执行...xid:{}",transId);
        AccountDTO accountDTO = new AccountDTO();
        //幂等判断 判断local_try_log表中是否有try日志记录，如果有则不再执行
        if(accountTccMapper.isExistTry(transId)>0){
            log.info("用户服务 try 已经执行，无需重复执行,xid:{}",transId);
            return accountDTO ;
        }
        //try悬挂处理，如果cancel、confirm有一个已经执行了，try不再执行
        if(accountTccMapper.isExistConfirm(transId)>0 || accountTccMapper.isExistCancel(transId)>0){
            log.info("账号服务 try悬挂处理  cancel或confirm已经执行，不允许执行try,xid:{}",transId);
            return accountDTO;
        }

        Account account = new Account();
        account.setPassword(PasswordUtil.generate(accountRegisterDTO.getPassword()));
        account.setUsername(accountRegisterDTO.getUsername());
        account.setMobile(accountRegisterDTO.getMobile());
        account.setDomain("c");
        account.setStatus(StatusCode.STATUS_OUT.getCode());
        Integer byAccount = accountMapper.createByAccount(account);
        accountDTO.setId(account.getId());
        accountDTO.setDomain(account.getDomain());
        accountDTO.setMobile(account.getMobile());
        accountDTO.setStatus(account.getStatus());
        accountDTO.setUsername(account.getUsername());

        return accountDTO;
    }

    /**
     * 事务提交操作
     */
    @Override
    public void confirmMethod(AccountRegisterDTO accountRegisterDTO) {
        String transId = HmilyTransactionContextLocal.getInstance().get().getTransId();
        //检查有没有提交操作
        if(accountTccMapper.isExistConfirm(transId)>0){
            log.info("账号服务  confirm 已经执行，无需重复执行...xid:{}",transId);
            return ;
        }
        System.out.println("事务提交操作");
        accountTccMapper.addConfirm(transId);
    }

    /**
     * 事务回滚操作
     */
    @Override
    public void cancelMethod(AccountRegisterDTO accountRegisterDTO) {
        //获取全局事务id
        String transId = HmilyTransactionContextLocal.getInstance().get().getTransId();
        if (accountTccMapper.isExistCancel(transId)>0){
            log.info("账号服务 cancel 已经执行，无需重复执行...xid:{}",transId);
            return;
        }
        log.info("账号服务 cancel begin 开始执行...xid:{}",transId);
        System.out.println("事务回滚操作");
        Account account = new Account();
        account.setMobile(accountRegisterDTO.getMobile());
        int flag = accountMapper.deleteByAccount(account);
    }


    /**
     登录功能
     @param accountLoginDTO 封装登录请求数据
     @return 用户及权限信息
     */
    @Override
    public AccountDTO login(AccountLoginDTO accountLoginDTO) {
        //1、查询用户，注意此处需要根据前端传递的 AccountLoginDTO的domain做判断：
        //  如果值是 c 表示是c端用户，需要根据手机号查询
        Account account =null;
        String c = "c";
        if (c.equalsIgnoreCase(accountLoginDTO.getDomain())){
            account = accountMapper.getMobileOne(accountLoginDTO.getMobile());
        }else {
            //  否则是b端用户，需要根据用户名查询
            account = accountMapper.selectByUsername(accountLoginDTO.getUsername());
        }
        //2、判断用户是否存在，如果不存在，直接抛出 AccountErrorCode.E_130104 异常
        if (account == null){
            throw new BusinessException(AccountErrorCode.E_130104);
        }
        //封装DTO
        AccountDTO accountDTO = convertAccountEntityToDTO(account);
        //3、如果 smsEnable=true ，说明是短信验证码登录，不做密码校验。直接返回
        if (smsEnable){
            return accountDTO;
        }
        //4、验证密码，如果密码错误则抛出 AccountErrorCode.E_130105 异常
        if (PasswordUtil.verify(accountLoginDTO.getPassword(),
                account.getPassword())){
            return accountDTO;
        }
        throw new BusinessException(AccountErrorCode.E_130105);
    }

    /**
     * 抽出来的方法
     * @param account 用户数据
     * @return 封装DTO返回
     */
    private AccountDTO convertAccountEntityToDTO(Account account){
        if (account == null){
            return null;
        }
        AccountDTO accountDTO = new AccountDTO();
        BeanUtils.copyProperties(account,accountDTO);
        return accountDTO;
    }

}
